﻿using DotNetCore.CAP;
using FluentAssertions;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using NewRetail.Application.Core;
using NewRetail.Application.Core.Entity;
using NewRetail.Application.Worker;
using Polly;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Volo.Abp.Application.Services;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.Json;
using Volo.Abp.Uow;

namespace NewRetail.Application.Components.Warehouse
{
    public class DataTypeDefinition
    {
        public DataTypeDefinition(IReadOnlyDictionary<string, Type> types)
        {
            types.Should().NotBeNull();
            this.Types = types;
        }

        public IReadOnlyDictionary<string, Type> Types { get; }
    }

    public interface IDataSync
    {
        Task HandleSync(DataSyncWrapper dataSyncWrapper);
    }

    public interface IOnDataSyncSuccess<T> where T : BaseEntity
    {
        Task OnDataSyncSuccess(T[] data);
    }

    public class DataSyncImpl<T> : IDataSync where T : BaseEntity
    {
        private readonly ILogger logger;
        private readonly IServiceProvider serviceProvider;
        private readonly IRepository<T> repo;
        private readonly IJsonSerializer jsonSerializer;
        private readonly IEntityHelper entityHelper;
        public DataSyncImpl(IRepository<T> repo,
            IServiceProvider serviceProvider,
            IJsonSerializer jsonSerializer, ILogger<DataSyncImpl<T>> logger,
            IEntityHelper entityHelper)
        {
            this.repo = repo;
            this.serviceProvider = serviceProvider;
            this.jsonSerializer = jsonSerializer;
            this.logger = logger;
            this.entityHelper = entityHelper;
        }

        async Task SaveWhenNotExist(T data)
        {
            var item = await this.repo.FirstOrDefaultAsync(x => x.Id == data.Id);
            if (item != null)
            {
                this.logger.LogInformation("数据存在，更新数据");

                item.UpdateEntity(data);

                this.entityHelper.SetUpdationInfo(item);
                await this.repo.UpdateAsync(item, autoSave: true);
            }
            else
            {
                this.logger.LogInformation("保存同步数据");
                this.entityHelper.SetCreationInfo(data);
                await this.repo.InsertAsync(data, autoSave: true);
            }
        }

        public async Task HandleSync(DataSyncWrapper dataSyncWrapper)
        {
            var items = this.jsonSerializer.Deserialize<T[]>(dataSyncWrapper.Payload);

            var policy = Policy.Handle<Exception>().WaitAndRetryAsync(3, i => TimeSpan.FromMilliseconds(100 * i));

            foreach (var m in items)
            {
                await policy.ExecuteAsync(() => this.SaveWhenNotExist(m));
            }

            using var s = this.serviceProvider.CreateScope();
            var subscribers = s.ServiceProvider.GetServices<IOnDataSyncSuccess<T>>();
            foreach (var sub in subscribers)
            {
                await sub.OnDataSyncSuccess(items);
            }
        }
    }

    public interface IDataSyncService : IApplicationService
    {
        Task QueueDataSync<T>(T[] data) where T : BaseEntity;
    }

    public class DataSyncService : MyApplicationService, IDataSyncService, ICapSubscribe
    {
        private readonly INewRetailEventBus newRetailEventBus;
        private readonly IJsonSerializer jsonSerializer;
        private readonly IUnitOfWorkManager unitOfWorkManager;
        public DataSyncService(INewRetailEventBus newRetailEventBus,
            IJsonSerializer jsonSerializer,
            IUnitOfWorkManager unitOfWorkManager)
        {
            this.newRetailEventBus = newRetailEventBus;
            this.jsonSerializer = jsonSerializer;
            this.unitOfWorkManager = unitOfWorkManager;
        }

        [CapSubscribe(MessageTopics.DATA_SYNC)]
        public async Task OnDataSync(DataSyncWrapper dataSyncWrapper)
        {
            using var uow = this.unitOfWorkManager.Begin(requiresNew: true);
            try
            {
                var define = this.LazyServiceProvider.LazyGetRequiredService<DataTypeDefinition>();

                var t = define.Types[dataSyncWrapper.DataType];

                if (t == null)
                {
                    this.Logger.LogInformation($"找不到类：{dataSyncWrapper.Payload}");
                    return;
                }

                var svc = this.LazyServiceProvider.LazyGetRequiredService(typeof(DataSyncImpl<>).MakeGenericType(t));
                if (svc is IDataSync s)
                {
                    await s.HandleSync(dataSyncWrapper);
                }
                else
                {
                    this.Logger.LogError("错误");
                }

                await uow.CompleteAsync();
            }
            catch (Exception e)
            {
                this.Logger.LogError(e, e.Message);
                await uow.RollbackAsync();
                throw;
            }
        }

        public async Task QueueDataSync<T>(T[] data) where T : BaseEntity
        {
            if (!data.Any())
            {
                return;
            }
            var message = new DataSyncWrapper()
            {
                DataType = typeof(T).GetTypeIdentity(),
                Payload = this.jsonSerializer.Serialize(data)
            };
            await this.newRetailEventBus.QueueDataSync(message);
        }
    }
}
